package com.yzx.yzxaop;

import com.yzx.yzxaop.annotation.*;
import com.yzx.yzxaop.annotation.Error;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.regex.Matcher;

/**
 * @author yzx
 * AOP类
 */
public class futureAOP {
    //切面类
    private static HashMap<String,Object> aspectMap = new HashMap<>();
    //切面类中的增强方法
    private static HashMap<String,Map<String, Method>> methodMap=new HashMap<>();
    //目标类
    private static HashMap<String, Class> objMap=new HashMap<>();


    public static void scanner(String path,String packageName) {
        List<String> paths = traverseFolder2(path);
        for (String p : paths) {
            p = p.substring(path.length() - 1);
//            System.out.println(p);
            try {
                String className = packageName + "." + p.replaceAll(Matcher.quoteReplacement(File.separator), ".");
                String replace = className.replace(".class", "");
//                System.out.println(replace);
//                System.out.println("-------------------------");
                Class<?> cl = ClassLoader.getSystemClassLoader().loadClass(replace);
                if(isAspect(cl)){

                    Aspect aspect = getAspect(cl);
                    if(aspectMap.get(aspect.value())!=null){
                        throw new RuntimeException("切面类"+aspect.value()+"已存在");
                    }else {
                        aspectMap.put(aspect.value(),cl.newInstance());
                        methodMap.put(aspect.value(),new HashMap<>());
                    }

                    Method[] methods = cl.getDeclaredMethods();
                    for(Method m:methods){
                        if(isBefore(m)){
                            methodMap.get(aspect.value()).put("before",m);
                        }
                        if(isAfter(m)){
                            methodMap.get(aspect.value()).put("after",m);
                        }
                        if(isError(m)){
                            methodMap.get(aspect.value()).put("error",m);
                        }
                    }
                }
                if (isTransaction(cl)){
                    if(objMap.get("target")!=null){
                        throw new RuntimeException("存在多个目标类");
                    }else {
                    objMap.put("target",cl);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        //输出测试
//       methodMap.forEach((k,v)->{
//           System.out.println(k+":"+v.toString());
//       });
//
//        System.out.println(objMap.get("target"));
    }


    public static Object getProxy(){
        if (objMap.isEmpty()){throw new RuntimeException("未找到目标类");}
        if(methodMap.isEmpty()){throw new RuntimeException("未找到切面类");}

        Class targetClass = objMap.get("target");//目标类的Class对象
        Class[] interfaces = targetClass.getInterfaces(); //获取目标类的接口
        Object t;//目标类实例
        try {
            t = targetClass.newInstance();//获取目标类的实例
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        String s = aspectMap.keySet().toArray()[0].toString(); //获取切面类的key
        Object o = aspectMap.get(s); //获取切面类
        Method BeforeM = methodMap.get(s).get("before");
        Method AfterM = methodMap.get(s).get("after");

        if (interfaces.length>0){
            return Proxy.newProxyInstance(targetClass.getClassLoader(), interfaces, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if(BeforeM!=null){
                        BeforeM.invoke(o);
                    }
                    Object invoke = method.invoke(t, args);
                    if(AfterM!=null){
                        AfterM.invoke(o);
                    }
                    return invoke;
                }
            });
        }else {
            System.out.println("检测到目标类未实现接口，采用cglib代理.....");
            Enhancer en = new Enhancer(); //帮我们生成代理对象
            en.setSuperclass(targetClass);//设置要代理的目标类
            en.setCallback(new MethodInterceptor() {//代理要做什么
                @Override
                public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    if(BeforeM!=null){BeforeM.invoke(o);}
                    Object invoke = methodProxy.invokeSuper(object, args);
                    // Object invoke = method.invoke(t, args);// 作用等同与上面。
                    if(AfterM!=null){AfterM.invoke(o);}
                    return invoke;
                }
            });
            return en.create();
        }
    }


    private static boolean isAspect(Class cl){
        Annotation annotation = cl.getAnnotation(Aspect.class);
        if(annotation!=null){
            return  true;
        }
        return false;
    }
    private static boolean isTransaction(Class cl){
        Annotation annotation = cl.getAnnotation(TransactionTest.class);
        if(annotation!=null){
            return  true;
        }
        return false;
    }
    private  static boolean isBefore(Method method){
        Annotation annotation = method.getAnnotation(Before.class);
        if(annotation!=null){
            return  true;
        }
        return false;
    }
    private  static boolean isAfter(Method method){
        Annotation annotation = method.getAnnotation(After.class);
        if(annotation!=null){
            return  true;
        }
        return false;
    }
    private  static boolean isError(Method method){
        Annotation annotation = method.getAnnotation(Error.class);
        if(annotation!=null){
            return  true;
        }
        return false;
    }

    private static Aspect getAspect(Class cl){
        Annotation annotation = cl.getAnnotation(Aspect.class);
        if(annotation instanceof  Aspect){
            return  (Aspect) annotation;
        }
        return null;
    }
//    private static Object getRequestMapping(Method method){
//        Annotation annotation = method.getAnnotation(RequestMapping.class);
//        if(annotation instanceof  RequestMapping){
//            return  (RequestMapping) annotation;
//        }
//        return null;
//    }


    private static List<String> traverseFolder2(String path) {
        File file = new File(path);
        List<String> classFiles=new ArrayList<>();
        if (file.exists()) {
            LinkedList<File> list = new LinkedList<File>();
            File[] files = file.listFiles();
            for (File file2 : files) {
                if (file2.isDirectory()) {
                    list.add(file2);
                } else {
                    classFiles.add(file2.getAbsolutePath());
                }
            }
            File temp_file;
            while (!list.isEmpty()) {
                temp_file = list.removeFirst();
                files = temp_file.listFiles();
                for (File file2 : files) {
                    if (file2.isDirectory()) {
                        list.add(file2);
                    } else {
                        classFiles.add(file2.getAbsolutePath());
                    }
                }
            }
        } else {

        }
        return classFiles;
    }

    //class END
}
